﻿#region File and License Information
/*
<File>
	<License Type="BSD">
		Copyright © 2009 - 2012, Outcoder. All rights reserved.
	
		This file is part of Calcium (http://calciumsdk.net).

		Redistribution and use in source and binary forms, with or without
		modification, are permitted provided that the following conditions are met:
			* Redistributions of source code must retain the above copyright
			  notice, this list of conditions and the following disclaimer.
			* Redistributions in binary form must reproduce the above copyright
			  notice, this list of conditions and the following disclaimer in the
			  documentation and/or other materials provided with the distribution.
			* Neither the name of the <organization> nor the
			  names of its contributors may be used to endorse or promote products
			  derived from this software without specific prior written permission.

		THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
		ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
		WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
		DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
		DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
		(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
		LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
		ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
		(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
		SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
	</License>
	<Owner Name="Daniel Vaughan" Email="danielvaughan@outcoder.com" />
	<CreationDate>2010-08-15 12:24:24Z</CreationDate>
</File>
*/
#endregion

using System;
using System.Collections.Generic;
using Microsoft.Practices.ServiceLocation;
using Outcoder.InversionOfControl;

namespace Outcoder
{
	/// <summary>
	/// This class is used to retrieve object instances, using type associations,
	/// and to create associations between types and object instances.
	/// This class is set to replace the ServiceLocatorSingleton.
	/// </summary>
	public static class Dependency
	{
#if USE_DEPRECATED_DEPENDCY_API
		/// <summary>
		/// Gets the service with the specified type.
		/// </summary>
		/// <param name="serviceType">Type of the service to retrieve.</param>
		/// <returns>The service instance.</returns>
		public static object GetService(Type serviceType)
		{
			return ServiceLocator.Current.GetService(serviceType);
		}

		public static object GetInstance(Type serviceType)
		{
			return ServiceLocator.Current.GetInstance(serviceType);
		}

		public static object GetInstance(Type serviceType, string key)
		{
			return ServiceLocator.Current.GetInstance(serviceType, key);
		}

		public static IEnumerable<object> GetAllInstances(Type serviceType)
		{
			return ServiceLocator.Current.GetAllInstances(serviceType);
		}

		public static TService GetInstance<TService>()
		{
			return ServiceLocator.Current.GetInstance<TService>();
		}

		public static bool TryGetInstance<TService>(out TService service)
		{
			var registrar = GetInstance<IDependencyRegistrar>();
			if (registrar.IsTypeRegistered(typeof(TService)))
			{
				service = ServiceLocator.Current.GetInstance<TService>();
				return true;
			}
			service = default(TService);
			return false;
		}

		[Obsolete("Use IsRegistered")]
		public static bool IsTypeRegistered<T>()
		{
			var registrar = GetInstance<IDependencyRegistrar>();
			return registrar.IsTypeRegistered(typeof(T));
		}

		[Obsolete("Use IsRegistered")]
		public static bool IsTypeRegistered(Type type)
		{
			var registrar = GetInstance<IDependencyRegistrar>();
			return registrar.IsTypeRegistered(type);
		}

		public static bool IsRegistered<T>()
		{
			var registrar = GetInstance<IDependencyRegistrar>();
			return registrar.IsTypeRegistered(typeof(T));
		}

		public static bool IsRegistered(Type type)
		{
			var registrar = GetInstance<IDependencyRegistrar>();
			return registrar.IsTypeRegistered(type);
		}

		/// <summary>
		/// Tries to retrieve the service with the specified instance type.
		/// </summary>
		/// <param name="instanceType">Type of the </param>
		/// <returns>The located instance, or <c>null</c> if not found.</returns>
		public static object TryGetInstance(Type instanceType)
		{
			ArgumentValidator.AssertNotNull(instanceType, "instanceType");

			var registrar = GetInstance<IDependencyRegistrar>();
			if (registrar.IsTypeRegistered(instanceType))
			{
				var service = ServiceLocator.Current.GetInstance(instanceType);
				return service;
			}
			return null;
		}

		public static TService GetInstance<TService, TDefaultImplementation>()
			where TDefaultImplementation : TService
		{
			TService service;
			var registrar = GetInstance<IDependencyRegistrar>();
			if (registrar.IsTypeRegistered(typeof(TService)))
			{
				service = ServiceLocator.Current.GetInstance<TService>();
			}
			else
			{
				service = ServiceLocator.Current.GetInstance<TDefaultImplementation>();
			}
			return service;
		}

		/// <summary>
		/// Attempts to retrieve the service with the specified type. 
		/// If not found, the specified default type will be used resolved,
		/// and it will be registered.
		/// </summary>
		/// <typeparam name="TService">The type of the service.</typeparam>
		/// <typeparam name="TDefaultImplementation">The type of the default implementation.</typeparam>
		/// <returns></returns>
		public static TService GetSingleton<TService, TDefaultImplementation>()
			where TDefaultImplementation : TService
		{
			TService service;
			var registrar = GetInstance<IDependencyRegistrar>();
			if (registrar.IsTypeRegistered(typeof(TService)))
			{
				service = ServiceLocator.Current.GetInstance<TService>();
			}
			else
			{
				service = ServiceLocator.Current.GetInstance<TDefaultImplementation>();
				RegisterInstance<TService>(service);
			}
			return service;
		}

		public static TService GetInstance<TService>(string key)
		{
			return ServiceLocator.Current.GetInstance<TService>(key);
		}

		public static IEnumerable<TService> GetAllInstances<TService>()
		{
			return ServiceLocator.Current.GetAllInstances<TService>();
		}

		public static void RegisterInstance<TService>(TService service)
		{
			var registrar = GetInstance<IDependencyRegistrar>();
			registrar.RegisterInstance<TService>(service);
		}

		public static void RegisterType<TFrom, TTo>() where TTo : TFrom
		{
			var registrar = GetInstance<IDependencyRegistrar>();
			registrar.RegisterType<TFrom, TTo>();
		}

		public static void RegisterSingleton<TFrom, TTo>() where TTo : TFrom
		{
			var registrar = GetInstance<IDependencyRegistrar>();
			registrar.RegisterSingleton<TFrom, TTo>();
		}

		public static void RegisterSingleton(Type fromType, Type toType)
		{
			var registrar = GetInstance<IDependencyRegistrar>();
			registrar.RegisterSingleton(fromType, toType);
		}
#else

		static IDependencyRegistrar registrarUseProperty;

		static IDependencyRegistrar Registrar
		{
			get
			{
				if (registrarUseProperty == null)
				{
					registrarUseProperty = Resolve<IDependencyRegistrar>();
				}

				return registrarUseProperty;
			}
		}

		/// <summary>
		/// Creates a type association between one type TTo, to another type TFrom;
		/// so that when the TFrom type is requested using e.g., <c>Resolve</c>, 
		/// an instance of the TTo type is returned. 
		/// </summary>
		/// <typeparam name="TFrom">The type forming the whole or partial key 
		/// for resolving the TTo type.</typeparam>
		/// <typeparam name="TTo">The associated type.</typeparam>
		/// <param name="key">The key. Can be <c>null</c>.</param>
		/// <param name="singleton">if set to <c>true</c> 
		/// only one instance will be created of the TTo type.</param>
		public static void Register<TFrom, TTo>(bool singleton = false, string key = null) 
			where TTo : TFrom
		{
			Registrar.Register<TFrom, TTo>(singleton, key);
		}
		
		public static void Register<TFrom>(TFrom instance, string key = null)
		{
			Registrar.Register<TFrom>(instance, key);
		}

		public static void Register(Type fromType, Type toType, bool singleton = false, string key = null)
		{
			Registrar.Register(fromType, toType, singleton, key);
		}

		public static void Register(Type fromType, object instance, string key = null)
		{
			Registrar.Register(fromType, instance, key);
		}

		public static void Register<T>(string key, bool singleton = false)
		{
			Registrar.Register<T, T>(singleton, key);
		}

		public static void Register<T>(Func<T> getInstanceFunc, bool singleton = false, string key = null)
		{
			Registrar.Register<T>(getInstanceFunc, singleton, key);
		}

		public static void Register(Type type, Func<object> getInstanceFunc, bool singleton = false, string key = null)
		{
			Registrar.Register(type, getInstanceFunc, singleton, key);
		}

		public static bool IsRegistered<T>()
		{
			Type fromType = typeof(T);
			return Registrar.IsTypeRegistered(fromType);
		}

		public static bool IsRegistered(Type fromType)
		{
			return Registrar.IsTypeRegistered(fromType);
		}

		public static T Resolve<T>(string key = null)
		{
			return ServiceLocator.Current.GetInstance<T>(key);
		}

		public static object Resolve(Type type)
		{
			ArgumentValidator.AssertNotNull(type, "type");
			return ServiceLocator.Current.GetInstance(type);
		}

		public static object Resolve(Type type, string key)
		{
			ArgumentValidator.AssertNotNull(type, "type");
			if (key != null)
			{
				return ServiceLocator.Current.GetInstance(type, key);
			}
			return ServiceLocator.Current.GetInstance(type);
		}

		public static IEnumerable<TFrom> ResolveAll<TFrom>()
		{
			return ServiceLocator.Current.GetAllInstances<TFrom>();
		}

		public static TFrom Resolve<TFrom, TDefaultImplementation>(
			bool singleton = true, string key = null)
			where TDefaultImplementation : TFrom
		{
			TFrom instance;
			if (Registrar.IsTypeRegistered(typeof(TFrom), key))
			{
				if (string.IsNullOrEmpty(key))
				{
					instance = ServiceLocator.Current.GetInstance<TFrom>();
				}
				else
				{
					instance = ServiceLocator.Current.GetInstance<TFrom>(key);
				}
			}
			else
			{
				instance = ServiceLocator.Current.GetInstance<TDefaultImplementation>();
				if (singleton)
				{
					Register<TFrom>(instance, key);
				}
				else
				{
					Register<TFrom, TDefaultImplementation>(false, key);
				}
			}
			return instance;
		}

		public static TFrom Resolve<TFrom>(
			TFrom defaultImplementation, bool singleton = true, string key = null)
		{
			Type fromType = typeof(TFrom);
			TFrom instance;

			if (Registrar.IsTypeRegistered(fromType, key))
			{
				if (string.IsNullOrEmpty(key))
				{
					instance = ServiceLocator.Current.GetInstance<TFrom>();
				}
				else
				{
					instance = ServiceLocator.Current.GetInstance<TFrom>(key);
				}
			}
			else
			{
				instance = defaultImplementation;
				if (singleton)
				{
					Register<TFrom>(defaultImplementation, key);
				}
				else
				{
					if (!object.Equals(defaultImplementation, default(TFrom)))
					{
						Register(fromType, instance.GetType());
					}
				}
			}
			return instance;
		}

		public static bool TryResolve<T>(out T result, string key = null)
			where T : class
		{
			try
			{
                /* When a developer has built Calcium on his or her machine, installed it, 
                 * and is building applications using Calcium, Visual Studio is over eager 
                 * in breaking on exceptions that occur and are handled within Calcium assemblies. 
                 * This can be rectified by turning of "Enable Just my code" 
                 * in Visual Studio options under Debugging. Checking to see if a type 
                 * is registered before resolving it, avoids an exception from being raised. */
                if (Registrar.IsTypeRegistered(typeof(T), key))
                {
                    result = Resolve<T>(key);
                }
                else
                {
                    result = null;
                }
			}
			catch (Exception) /* Unable to be more specific because 
							   * we don't know the container implementation. */
			{
				result = null;
			}
			return result != null;
		}

		public static bool TryResolve(Type type, out object result, string key = null)
		{
			try
			{
                /* When a developer has built Calcium on his or her machine, installed it, 
                 * and is building applications using Calcium, Visual Studio is over eager 
                 * in breaking on exceptions that occur and are handled within Calcium assemblies. 
                 * This can be rectified by turning of "Enable Just my code" 
                 * in Visual Studio options under Debugging. Checking to see if a type 
                 * is registered before resolving it, avoids an exception from being raised. */
			    if (Registrar.IsTypeRegistered(type, key))
			    {
                    result = Resolve(type, key);
			    }
			    else
			    {
			        result = null;
			    }
			}
			catch (Exception) /* Unable to be more specific because 
							   * we don't know the container implementation. */
			{
				result = null;
			}
			return result != null;
		}

#if !WINDOWS_PHONE
		static volatile bool initialized;

		public static bool Initialized
		{
			get
			{
				return initialized;
				//return UnityExtensions.Initialized;
			}
			set
			{
				initialized = value;
				//UnityExtensions.Initialized = value;
			}
		}
#else
		static bool initialized;

		public static bool Initialized
		{
			get
			{
				return initialized;
			}
			internal set
			{
				initialized = value;
			}
		}
#endif

#endif
	}
}
